import base64
import hashlib
import hmac

__author__ = 'Sea'

class DataSecurityError(Exception):
    pass

class SHA256(object):
    """
    Security about parameters post across HTTP protocol.
    """
    UTF8 = 'utf-8'
    EQUAL = '='

    @classmethod
    def _sha256_encode_checking(cls,encoded_value,secret):
        if encoded_value and secret:
            try :
                string_list = None
                if isinstance(encoded_value,str) and encoded_value.index(r'.') != -1:
                    string_list = encoded_value.split(r'.',2)
                if string_list[0]:
                    padding = (4 - (len(string_list[0]) % 4))
                    while padding:
                        string_list[0] += cls.EQUAL
                        padding -= 1
                    string_list[0] = string_list[0].replace('-','+').replace('_','/')
                    data = string_list[1]
                    if isinstance(secret,unicode):
                        secret = secret.encode(cls.UTF8)
                    if isinstance(data,unicode):
                        data = data.encode(cls.UTF8)
                    except_value = hmac.new(secret, msg=data.strip(), digestmod=hashlib.sha256).digest()
                    except_value = base64.encodestring(except_value)
                    if isinstance(string_list[0],unicode):
                        string_list[0] = string_list[0].encode(cls.UTF8)
                    except_value = except_value.encode(cls.UTF8).strip()
                    string_list[0] = string_list[0].strip()
                    return string_list[0] == except_value
            except ValueError as e:
                return False
            except IndexError as e:
                return False
        return False

    @classmethod
    def sha256_decode(cls,encoded_value,secret=None,security=True):
        """
        Decode sha256 secreted information.
        encoded_value target secreted information
        secret sha256 secret
        security when decode if check secreted information security
        """
        if encoded_value:
            if isinstance(encoded_value,unicode):
                encoded_value = encoded_value.encode(cls.UTF8)
            encoded_value = str(encoded_value).strip()
            if secret:
                if isinstance(secret,unicode):
                    secret = secret.encode(cls.UTF8)
                secret = str(secret).strip()
            if security:
                #check if the secreted value is changed
                if not cls._sha256_encode_checking(encoded_value,secret):
                    raise DataSecurityError('The secreted information is not security.')
                    #decode secreted value directly
            try:
                #secreted information are two parts which split by "."
                _list = encoded_value.split('.')
                padding = 4-(len(_list[1]) % 4)
                #first part is sha256 secreted information
                #second part is base64 encoded information, is request information also
                part2 = _list[1]
                #post information across HTTP protocol, will drop "=", so append again
                while padding:
                    part2 = '%s%s' % (part2,cls.EQUAL)
                    padding -= 1
                value = base64.decodestring(part2)
                return value
            except IndexError as e:
                raise DataSecurityError('Security string format bad. %s' % e)

    @classmethod
    def sha256_encode(cls,target_value,secret):
        """sha256 encode security information"""
        target = None
        if target_value and secret:
            if isinstance(target_value,unicode):
                target_value = target_value.encode(cls.UTF8)
            target_value = str(target_value).strip()
            if isinstance(secret,unicode):
                secret = secret.encode(cls.UTF8)
            secret = str(secret).strip()
            t = base64.encodestring(target_value)
            if cls.EQUAL in t:
                t = t[0:t.index('=')]
            target = t
            if isinstance(t,unicode):
                t = t.encode(cls.UTF8)
            t = hmac.new(key=secret, msg=t.strip(), digestmod=hashlib.sha256).digest()
            t = base64.encodestring(t)
            t = t.replace('+','-').replace('/','_')
            if cls.EQUAL in t:
                t = t[0:t.index(cls.EQUAL)]
            target = '%s.%s' % (t,target)
        if target:
            try:
                if target.endswith('\n'):
                    target = target[0:target.index('\n')]
            except Exception as e:
                target = None
        return target
